/**
 *  \file character.cpp
 *  \brief Curses++ character implementation
 *
 *  This file contains all the code needed. Yay.
 *
 *  \if LICENSE
 *  Curses++ - Free C++ wrapper to the curses library.
 *  Copyright (C) 2007 Sean Hunt
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330,
 *  Boston, MA 02111-1307, USA.
 *  \endif
 */

#include "cursesplusplus.h"
#include "curses-local.hpp"

namespace curses
{
	namespace
	{
		character::color fg0 = character::white;
		character::color bg0 = character::black;

		character::color curse_to_color (short int c)
		{
			switch (c)
			{
				case COLOR_BLACK: return character::black;
				case COLOR_RED: return character::red;
				case COLOR_YELLOW: return character::yellow;
				case COLOR_GREEN: return character::green;
				case COLOR_BLUE: return character::blue;
				case COLOR_CYAN: return character::cyan;
				case COLOR_MAGENTA: return character::magenta;
				case COLOR_WHITE: return character::white;
				// This shouldn't happen.
				default: return character::white;
			}
		}

		short int color_to_curse (character::color c)
		{
			switch (c)
			{
				case character::black: return COLOR_BLACK;
				case character::red: return COLOR_RED;
				case character::yellow: return COLOR_YELLOW;
				case character::green: return COLOR_GREEN;
				case character::blue: return COLOR_BLUE;
				case character::cyan: return COLOR_CYAN;
				case character::magenta: return COLOR_MAGENTA;
				default: return COLOR_WHITE;
			}
		}
	}

	unsigned char get_pair(character::color fg, character::color bg)
	{
		// Current terminal background. Always at index 0.
		if (fg == fg0 && bg == bg0)
			return 0;
		// Black-on-black is normally 0, but that's the background. Use the index normally assigned to the background.
		if (fg == character::black && bg == character::black)
			return fg0 * 8 + bg0;
		// No other special code required.
		return fg * 8 + bg;
	}

	std::pair<character::color, character::color> pair_contents(unsigned char pair)
	{
		if (pair == 0)
			return std::make_pair(fg0, bg0);
		unsigned char fg = pair / 8;
		unsigned char bg = pair % 8;
		if (fg == fg0 && bg == bg0)
			return std::make_pair(character::black, character::black);
		return std::make_pair(static_cast<character::color>(fg), static_cast<character::color>(bg));
	}

	void prepare_color()
	{
		start_color();
		short fg, bg;

		pair_content(0, &fg, &bg);
		fg0 = curse_to_color(fg);
		bg0 = curse_to_color(bg);

		for (unsigned char i = 0; i < 8; ++i)
			for (unsigned char j = 0; j < 8; ++j)
			{
				// Color pair 0 is already initted.
				if (!(i + j))
					continue;
				// Special color pair acting as black on black
				if (i == fg0 && j == bg0)
					init_pair(i * 8 + j, COLOR_BLACK, COLOR_BLACK);
				else
					init_pair(i * 8 + j, color_to_curse(static_cast<character::color>(i)), color_to_curse(static_cast<character::color>(j)));
			}
	}

	void prepare_locale()
	{
		std::locale locale = std::locale();
		locale = std::locale(locale, new std::ctype<character>);
		locale = std::locale(locale, new std::codecvt<character, char, character::iostate>);
	}

	chtype get_curses (const character& c)
	{
		chtype ch = c.get_char() | COLOR_PAIR(get_pair(c.get_fg(), c.get_bg()));
		character::attribute attr = c.get_attr();

		if (attr & character::alternate)
			ch |= A_ALTCHARSET;
		if (attr & character::blink)
			ch |= A_BLINK;
		if (attr & character::bold)
			ch |= A_BOLD;
		if (attr & character::dim)
			ch |= A_DIM;
		if (attr & character::invisible)
			ch |= A_INVIS;
		if (attr & character::protect)
			ch |= A_PROTECT;
		if (attr & character::reverse)
			ch |= A_REVERSE;
		if (attr & character::standout)
			ch |= A_STANDOUT;
		if (attr & character::underline)
			ch |= A_UNDERLINE;

		return ch;
	}

	character from_curses (chtype ch)
	{
		character c = ch & A_CHARTEXT;
		std::pair<character::color, character::color> pair = pair_contents(PAIR_NUMBER(ch & A_COLOR));
		c.set_fg(pair.first);
		c.set_bg(pair.second);

		if (ch & A_ALTCHARSET)
			c |= character::alternate;
		if (ch & A_BLINK)
			c |= character::blink;
		if (ch & A_BOLD)
			c |= character::bold;
		if (ch & A_DIM)
			c |= character::dim;
		if (ch & A_INVIS)
			c |= character::invisible;
		if (ch & A_PROTECT)
			c |= character::protect;
		if (ch & A_REVERSE)
			c |= character::reverse;
		if (ch & A_STANDOUT)
			c |= character::standout;
		if (ch & A_UNDERLINE)
			c |= character::underline;

		return c;
	}
}
